package com.bluse.config;


import com.bluse.client.HttpClient;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.convert.KeyspaceConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;


@Configuration
@EnableScheduling
public class HttpClientConfig {

    private static final Logger logger = LoggerFactory.getLogger(HttpClientConfig.class);

    // Determines the  timeout in milliseconds until a  connection is established
    private static final int CONNECT_TIMEOUT = 10000;

    //the timeout when requesting a connection from  the connection manager
    private static final int REQUEST_TIMEOUT = 30000;

    //the timeout for waiting for data by socket channel
    private static final int SOCKET_TIMEOUT = 60000;

    //the request can create  max total conneections ;
    private static final int MAX_TOTAL_CONNECTIONS = 50;

    private static final int DEFAULT_KEEPAILVE_MILLIS = 20 * 1000;

    private static final int CLOSE_IDLD_CONNECTION_WAIT_TIME_SECS = 30;

    @Bean
    public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
        SSLContextBuilder builder = new SSLContextBuilder();
        try {
            builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
        } catch (NoSuchAlgorithmException | KeyStoreException e) {
            logger.error("Polling Connection Management Intialisation  failrue because of: msg={},e={}", e.getMessage(), e);
        }

        SSLConnectionSocketFactory sslf = null;
        try {
            sslf = new SSLConnectionSocketFactory(builder.build());
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            logger.error("Polling Connection Management Intialisation  failure because of: msg={},e={}", e.getMessage(), e);
        }

        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("https", sslf)
                .register("http", new PlainConnectionSocketFactory())
                .build();

        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        poolingHttpClientConnectionManager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
        return poolingHttpClientConnectionManager;
    }


    @Bean
    public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {

        return (response, context) -> {
            HeaderElementIterator iterator = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
            while (iterator.hasNext()) {
                HeaderElement header = iterator.nextElement();
                String param = header.getName();
                String value = header.getValue();
                if (value != null && param.equalsIgnoreCase("timeout")) {
                    return Long.parseLong(value) * 1000;
                }
            }
            return DEFAULT_KEEPAILVE_MILLIS;
        };
    }

    @Bean
    public CloseableHttpClient httpClient() {
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(REQUEST_TIMEOUT)
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT)
                .setAuthenticationEnabled(true)
                .build();

        return HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(poolingHttpClientConnectionManager())
                .setKeepAliveStrategy(connectionKeepAliveStrategy())
                .build();

    }

    @Bean
    public Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager clientConnectionManager) {
        return new Runnable() {
            @Override
            @Scheduled(fixedRate = 10000)
            public void run() {
                try {
                    if (clientConnectionManager != null) {
                        logger.trace("run  IdleConnectionMonitor - Closing expired and idle connections...");
                        clientConnectionManager.closeExpiredConnections();
                        clientConnectionManager.closeIdleConnections(CLOSE_IDLD_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS);
                    } else {
                        logger.trace("run IdleConnectionMonitor - Http Client connection manager is not Initialised");
                    }
                } catch (Exception e) {
                    logger.error("run IdleConnectionMonitor - Exception ocurred . msg={} ,e={}", e.getMessage(), e);
                }
            }
        };
    }


}
